package com.xiaoxiang.cameracontrol.zsocket;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.logging.Level; 
import java.util.logging.Logger;

/**
 * 
 * 使用Socket进行通信的Client类。
 * 全双工通信，使用了额外的包处理技术防止粘包问题，包含自动重连机制。<br><br>
 * 使用时继承SocketClient类，必须重写的函数有：<br>
 * void connected();<br>
 * void disconnected();<br>
 * void recv_message();<br><br>
 * 使用connect()函数与服务器连接。向服务器端发送数据请使用：<br>
 * void send_bytes(byte[] data);<br>
 * void send_string(String data);<br><br>
 * 
 * 
 * @author XiaoXiang
 *
 */
public abstract class SocketClient extends Thread{
	private int reconnect_time = 5;
	protected Logger logger=null;
	protected String host = null;
	protected int port = -1;
	private boolean should_close = false;
	protected Socket socket = null;
	protected InputStream socket_input;
	protected OutputStream socket_output;
	
	public SocketClient(int reconnect) {
		this.reconnect_time = reconnect;
		this.init_logger();
	}
	public SocketClient() {
		 this.init_logger();
	}
	
	private void init_logger() {
		this.logger = Logger.getLogger(SocketClient.class.getName());
		this.logger.setLevel(Level.INFO);
	}
	
	/***
	 * 与服务器建立连接函数
	 * @param ip 服务器的IP地址
	 * @param port 服务器端口
	 */
	public void connect(String ip, int port) {
		this.host = ip;
		this.port = port;
		if(this.connectServer()) {
			this.start();
		}else {
			this.reconnect();
		}
	}
	
	private boolean connectServer() {
		this.socket = new Socket();
		try {
			this.socket.connect(new InetSocketAddress(this.host, this.port), 5000);
			this.socket_input = this.socket.getInputStream();
			this.socket_output = this.socket.getOutputStream();
			this.logger.log(Level.INFO, "已连接上"+this.host+":"+String.valueOf(this.port));
			this.connected();
		} catch (UnknownHostException e) {
			return false;
		} catch (IOException e) {
			return false;
		}
		return true;
	}
	
	private boolean reconnect() {
		if(this.reconnect_time==0) return false;
		int sleep_time = 5000;
		for(int i=0;i<this.reconnect_time; i++) {
			this.logger.info("正在尝试第"+String.valueOf(i)+"次重连");
			if(this.connectServer()) {
				this.should_close = false;
				this.start();
				this.logger.info("已经与服务器重新连接");
				this.connected();
				return true;
			}else {
				try {
					Thread.sleep(sleep_time);
				} catch (InterruptedException e) {}
				sleep_time *=2;
				continue;
			}
		}
		this.logger.warning("经过多次尝试，仍然连接服务器失败");
		return false;
	}
	
	/**
	 * 与服务器连接上后的回调函数，在自动重连成功后，该函数也会被调用
	 */
	abstract protected void  connected();
	
	/**
	 *  与服务器断开连接后的回调函数。
	 */
	abstract protected void disconnected();
	
	/**
	 * 收到从服务器发送过来的消息时产生的回调函数，传入的message数据必定是服务器发送过来的一条完整的数据。
	 * @param message 从服务器接收到的一条完整数据
	 */
	abstract protected void recv_message(byte[] message);
	
	/**
	 * 主动断开与服务器的连接
	 */
	public void close() {
		this.should_close = true;
		try {
			this.socket_input.close();
			this.socket_output.close();
			this.socket_input=null;
			this.socket_output=null;
			this.socket.close();
			
		}catch(Exception e) {
			
		}
		this.socket = null;
		this.disconnected();
	}
	
	/**
	 * 向服务器发送byte[]类型的数据
	 * @param data 待发送的数据
	 */
	public void send_bytes(byte[] data) {
		if(this.socket!=null) {
			byte[] length = FrameMessage.int_to_bytes(data.length, "big");
			try {
				OutputStream out = this.socket_output;
				out.write(length);
				out.write(data);
				out.flush();
			}catch(Exception e) {
				this.logger.warning("发送数据失败。"+e.getMessage());
			}
		}
	}
	
	/**
	 * 向服务器发送String类型的数据
	 * @param data 待发送的数据
	 */
	public void send_string(String data) {
		try {
			this.send_bytes(data.getBytes("UTF-8"));
		} catch (UnsupportedEncodingException e) {
			this.logger.warning("字符串无法进行UTF-8编码，发送失败: "+data+"\n"+e.getMessage());
		}
	}
	
	public void run() {
		byte[] buffer = new byte[0];
		this.logger.info("数据接收线程已开启");
		while(this.should_close==false) {
			byte[] data = new byte[5];
			try {
				InputStream in = this.socket_input;
				int count = in.read(data);
				if(count==0) {
					this.logger.info("未读取到任何数据");
					continue;
				}
				data = FrameMessage.sub_list(data, 0, count);
				buffer = FrameMessage.att_list(buffer, data);
				while(true) {
					FrameMessage m = new FrameMessage(buffer);
					buffer = m.get_remain_message();
					if(m.get_complete_message().length>0) {
						this.recv_message(m.get_complete_message());
					}else break;
				}
				
			} catch (IOException e) {
				if(this.should_close==true) break;
				else {
					this.logger.warning("连接异常，即将重连"+e.getMessage());
					this.connectServer();
				}
			}
			
		}
	}
	
	
	
}
